package org.apache.ibatis.common.reflection.factory;

import org.apache.ibatis.common.reflection.Reflector;
import org.apache.ibatis.common.reflection.ReflectorException;

import javax.management.ReflectionException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Name DefaultObjectFactory
 * @Description: 对象生产工厂接口 [实现] 类
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-04-25 14:22:36
 **/
public class DefaultObjectFactory implements ObjectFactory, Serializable {
    private static final long serialVersionUID = 4844251832003518630L;

    @Override
    public <T> T create(Class<T> type) {
        return create(type, null, null);
    }

    @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        Class<?> targetClass = resolveInterface(type);
        return (T) instantiate(targetClass, constructorArgTypes, constructorArgs);
    }

    /**
     * 创建类的实例
     *
     * @param targetClass         目标类
     * @param constructorArgTypes 构造参数类型列表
     * @param constructorArgs     构造参数值列表
     * @return java.lang.Object 结果
     * @version v1.0.0
     * @author lichao
     * @date 2024-04-25 14:33:17
     */
    private <T> T instantiate(Class<T> targetClass, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        try {
            // 声明构造器
            Constructor<T> constructor;
            // 1. 使用无参构造器创建实例
            if (constructorArgTypes == null || constructorArgs == null) {
                constructor = targetClass.getDeclaredConstructor();
                try {
                    return constructor.newInstance();
                } catch (IllegalAccessException e) {
                    // 如果发生异常，则修改无参构造器的访问属性后再次尝试创建对象
                    if (Reflector.canControlMemberAccessible()) {
                        constructor.setAccessible(true);
                        return constructor.newInstance();
                    } else {
                        throw e;
                    }
                }
            }
            // 2. 根据参数类型列表，查找对应的构造器，并使用有参构造器创建实例
            constructor = targetClass.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
            try {
                return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
            } catch (IllegalAccessException e) {
                // 如果发生异常，则修改有参构造器的访问属性后再次尝试创建对象
                if (Reflector.canControlMemberAccessible()) {
                    constructor.setAccessible(true);
                    return constructor.newInstance(constructorArgs.toArray(new Object[]{constructorArgs.size()}));
                } else {
                    throw e;
                }
            }
        } catch (Exception e) {
            // 如果发生异常，收集所有的参数类型和参数值，抛出异常信息
            String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList).stream().map(Class::getSimpleName).collect(Collectors.joining(","));
            String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList).stream().map(String::valueOf).collect(Collectors.joining(","));
            throw new ReflectorException("实例化类 [" + targetClass + "] 的对象失败，无效的参数类型 (" + argTypes + ")，或者参数值 (" + argValues + ")，失败原因：" + e, e);
        }
    }

    /**
     * 判断需要创建的目标对象的类型，即
     * - 如果传入的是接口，则给出它的一种实现类型
     * - 如果传入的是类，则直接返回
     *
     * @param type 传入类型
     * @return java.lang.Class<?> 结果
     * @version v1.0.0
     * @author lichao
     * @date 2024-04-25 14:26:42
     */
    protected <T> Class<?> resolveInterface(Class<T> type) {
        Class<?> targetClass;
        if (type == List.class || type == Collection.class || type == Iterable.class) targetClass = ArrayList.class;
        else if (type == Map.class) targetClass = HashMap.class;
        else if (type == Set.class) targetClass = HashSet.class;
        else if (type == SortedSet.class) targetClass = TreeSet.class;
        else targetClass = type;
        return targetClass;
    }

    @Override
    public <T> boolean isCollection(Class<T> type) {
        return Collection.class.isAssignableFrom(type);
    }
}

